Imports System
Imports System.Xml
Imports System.Text
Imports System.IO

'''************************************************************************
''' SharePoint log viewer
''' Developed by Djamel Chagour
''' Version : 1.0 
''' 01-01-2010 - Happy new year! 
'''
''' <summary>
''' A class for loading a SharePoint log file into a Grid view that it can be viewed and filtered
''' </summary>
''' <remarks></remarks>
Public Class MossLogViewer
    Private MossLogPath = "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\LOGS\"
    Private MossLogFile As String
    Private XmlLogFile As IO.FileStream
    Private LogWriter As XmlTextWriter
    Private strNomFichier As String
    Private startDate As Date = CDate(FormatDateTime(Now, DateFormat.ShortDate).ToString + " 00:00:01")
    Private endDate As Date = CDate(FormatDateTime(Now, DateFormat.ShortDate).ToString + " 23:59:00")
    Dim FilterActive As Boolean = False
    Dim DGBindingSource As BindingSource

    Private Sub LoadMossLog()
        'Initialize the values of the form controls
        InitialiseProcess()
        'Convert the sharePoint log file to a xml file and load it to the GridView
        CreateXmlLog()
    End Sub
    ''' <summary>
    ''' Load the SharePoint txt log file, convert it to a xml file then bind it to that Grid control.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub CreateXmlLog()
        ' Load the log file into a txt reader
        Dim txtReader As StreamReader = File.OpenText(strNomFichier)
        Dim strLine As String
        Dim cTimeStamp As String
        Dim dTimeStamp As Date
        Dim cProcess As String
        Dim cTID As String
        Dim cArea As String
        Dim cCategory As String
        Dim cEventID As String
        Dim cLevel As String
        Dim cMessage As String
        Dim intLine As Integer = 0
        Dim intProgress = 0
        'Create the xml file
        XmlLogFile = File.Create("C:\TEMP\SPLOG.xml")
        LogWriter = New XmlTextWriter(XmlLogFile, Encoding.UTF8)
        LogWriter.Formatting = Formatting.Indented
        LogWriter.WriteProcessingInstruction("xml", "version=""1.0"" encoding=""utf-8""")
        LogWriter.WriteStartElement("Lines")

        Try
            'Iterate through all the lines of the reader
            While txtReader.Peek <> -1
                intLine += 1
                strLine = txtReader.ReadLine()
                strLine = strLine.Replace(ControlChars.Tab, ";")

                'Set the value of the progress bar
                LogProgressBar.Visible = True
                LogProgressBar.Maximum = 100
                intProgress = (txtReader.BaseStream.Position * 100) / txtReader.BaseStream.Length
                LogProgressBar.Value = intProgress
                If intLine > 1 Then
                    Dim cLine As String() = strLine.Split(CChar(";"))
                    cTimeStamp = cLine(0).Trim()
                    cTimeStamp = cTimeStamp.Substring(0, cTimeStamp.IndexOf("."))
                    dTimeStamp = Convert.ToDateTime(cTimeStamp)
                    cProcess = cLine(1).Trim()
                    cTID = cLine(2).Trim()
                    cArea = cLine(3).Trim()
                    cCategory = cLine(4).Trim()
                    cEventID = cLine(5).Trim()
                    cLevel = cLine(6).Trim()
                    cMessage = cLine(7).Trim() + " " + cLine(8).Trim()

                    'Set the values of the combo boxes that will be used for filtering
                    FillComboBox(cmbProcess, cProcess)
                    FillComboBox(cmbArea, cArea)
                    FillComboBox(cmbCategorie, cCategory)
                    FillComboBox(cmbLevel, cLevel)

                    'Create the Line element of the xml file
                    LogWriter.WriteStartElement("Line")
                    LogWriter.WriteAttributeString("TimeStamp", dTimeStamp)
                    LogWriter.WriteAttributeString("Process", cProcess)
                    LogWriter.WriteAttributeString("TID", cTID)
                    LogWriter.WriteAttributeString("Area", cArea)
                    LogWriter.WriteAttributeString("Categorie", cCategory)
                    LogWriter.WriteAttributeString("EventID", cEventID)
                    LogWriter.WriteAttributeString("Level", cLevel)
                    LogWriter.WriteAttributeString("Message", cMessage)
                    LogWriter.WriteEndElement()
                End If
            End While
            txtReader.Close()
            LogWriter.WriteEndElement() 'End of element 

            'Write the file to disk and close the writer
            LogWriter.Flush()
            LogWriter.Close()
            LogWriter = Nothing
            LogProgressBar.Visible = False

            'Bind the xml file created to the Grid
            BindXmlLogToDataGridView(XmlLogFile)
        Catch ex As Exception
            'Show the exception and close the xml file
            MsgBox(ex.Message.ToString, MsgBoxStyle.Critical, "Error")
            LogWriter.Close()
            LogWriter = Nothing
        End Try
    End Sub
    ''' <summary>
    ''' Method to bind the xml log file to the Grid
    ''' </summary>
    ''' <param name="xmlLogFileName"></param>
    ''' <remarks></remarks>
    Private Sub BindXmlLogToDataGridView(ByVal xmlLogFileName)
        Dim LogDataSet As New DataSet()
        Try
            LogDataSet.ReadXml("C:\TEMP\SPLOG.xml")
            With DGLog
                DGBindingSource = New BindingSource()
                DGBindingSource.DataSource = LogDataSet
                DGBindingSource.DataMember = LogDataSet.Tables(0).TableName
                DGLog.DataSource = DGBindingSource
                DGLog.Columns("TimeStamp").Width = 140
                DGLog.Columns("Process").Width = 150
                DGLog.Columns("TID").Width = 60
                DGLog.Columns("Area").Width = 180
                DGLog.Columns("Categorie").Width = 180
                DGLog.Columns("EventID").Width = 60
                DGLog.Columns("Level").Width = 75
                DGLog.Columns("Message").Width = 0
            End With
        Catch ex As Exception
            MsgBox(ex.Message.ToString, MsgBoxStyle.Critical, "Error")
        End Try
    End Sub

    ''' <summary>
    ''' Display the log message of the line selected in the message text box
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub DGLog_SelectionChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DGLog.SelectionChanged
        Try
            txtMessage.Text = DGLog.CurrentRow.Cells("Message").Value.ToString
        Catch ex As Exception
        End Try
    End Sub

    ''' <summary>
    ''' Method of the button Open
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub btnOpen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpen.Click
        Dim Logfdlg As New OpenFileDialog
        Logfdlg.Title = "Open the log File to process"
        Logfdlg.InitialDirectory = MossLogPath
        Logfdlg.Filter = "Log Files|*.log|All Files|*.*"
        Logfdlg.FilterIndex = 2
        Logfdlg.RestoreDirectory = True
        If Logfdlg.ShowDialog() = Windows.Forms.DialogResult.OK Then
            MossLogFile = Logfdlg.FileName
            If CanReadFile(MossLogFile) Then
                strNomFichier = MossLogFile
            Else
                'If the file choosen to be open is currently used by SharePoint then make a copy 
                Dim tmpLogFile As String = MossLogFile.Substring(MossLogFile.LastIndexOf("\") + 1, MossLogFile.Length - MossLogFile.LastIndexOf("\") - 1)
                File.Copy(MossLogFile, "C:\temp\" + tmpLogFile, True)
                strNomFichier = "C:\temp\" + tmpLogFile
            End If
            If IsSPLogFile(strNomFichier) Then
                StatusFilename.Text = "Log file : " & MossLogFile.Substring(MossLogFile.LastIndexOf("\") + 1, MossLogFile.Length - MossLogFile.LastIndexOf("\") - 1)
                LoadMossLog()
            Else
                MsgBox("The file you are trying to open is not a valid SharePoint log file", MsgBoxStyle.Exclamation, "Open log file")
            End If
        End If
    End Sub
    ''' <summary>
    ''' This method is used to check if the log file is currently used by SharePoit
    ''' </summary>
    ''' <param name="strFileName"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function CanReadFile(ByVal strFileName As String) As Boolean
        Dim ret As Boolean = False
        Try
            Dim fs As FileStream = New FileStream(strFileName, FileMode.Open)
            ret = True
            fs.Close()
            Return ret
        Catch ex As Exception
        End Try
    End Function

    ''' <summary>
    ''' Method used to apply the filter
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub btnFilter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFilter.Click
        ApplyFilter()
    End Sub
    ''' <summary>
    ''' Method used to delete the filter currently applied
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub btnUnfilter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUnfilter.Click
        If String.IsNullOrEmpty(strNomFichier) Then
            MsgBox("No log file open yet!", MsgBoxStyle.Critical, "Error")
            Exit Sub
        End If
        DGBindingSource.RemoveFilter()
        StatusFilter.Text = "Filter : none"
    End Sub
    ''' <summary>
    ''' Method to set the filter parameters
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub ApplyFilter()
        Dim strFilter As String = String.Empty
        If String.IsNullOrEmpty(strNomFichier) Then
            MsgBox("No log file open yet!", MsgBoxStyle.Critical, "Error")
            Exit Sub
        End If
        If txtStartHour.Text <> String.Empty Then
            If Convert.ToInt16(txtStartHour.Text) > 23 Then txtStartHour.Text = "00"
            If txtStartmn.Text = String.Empty Then txtStartmn.Text = "00"
            startDate = CDate(FormatDateTime(LogDTPicker.Value, DateFormat.ShortDate).ToString + " " + txtStartHour.Text + ":" + txtStartmn.Text)
            strFilter = "TimeStamp >='" + startDate + "'"
        End If
        If txtEndHour.Text <> String.Empty Then
            If Convert.ToInt16(txtEndHour.Text) > 23 Then txtEndHour.Text = "23"
            If txtEndmn.Text = String.Empty Then txtEndmn.Text = "59"
            endDate = CDate(FormatDateTime(LogDTPicker.Value, DateFormat.ShortDate).ToString + " " + txtEndHour.Text + ":" + txtEndmn.Text)
            If strFilter = String.Empty Then
                strFilter = "TimeStamp<='" + endDate + "'"
            Else
                strFilter = strFilter + " and TimeStamp<='" + endDate + "'"
            End If
        End If
        If cmbProcess.Text <> String.Empty And cmbProcess.Text <> "All" Then
            If strFilter = String.Empty Then
                strFilter = "Process='" + cmbProcess.Text + "'"
            Else
                strFilter = strFilter + " and Process='" + cmbProcess.Text + "'"
            End If
        End If
        If cmbArea.Text <> String.Empty And cmbArea.Text <> "All" Then
            If strFilter = String.Empty Then
                strFilter = "Area='" + cmbArea.Text + "'"
            Else
                strFilter = strFilter + " and Area='" + cmbArea.Text + "'"
            End If
        End If
        If cmbCategorie.Text <> String.Empty And cmbCategorie.Text <> "All" Then
            If strFilter = String.Empty Then
                strFilter = "Categprie='" + cmbCategorie.Text + "'"
            Else
                strFilter = strFilter + " and Categorie='" + cmbCategorie.Text + "'"
            End If
        End If
        If cmbLevel.Text <> String.Empty And cmbLevel.Text <> "All" Then
            If strFilter = String.Empty Then
                strFilter = "Level='" + cmbLevel.Text + "'"
            Else
                strFilter = strFilter + " and Level='" + cmbLevel.Text + "'"
            End If
        End If
        'Apply the filter
        DGBindingSource.Filter = strFilter
        StatusFilter.Text = "Filter : " & strFilter
        FilterActive = True

    End Sub
    ''' <summary>
    ''' Method to initialize the form controls
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub InitialiseProcess()
        cmbProcess.Items.Clear()
        cmbArea.Items.Clear()
        cmbCategorie.Items.Clear()
        cmbLevel.Items.Clear()
        txtStartHour.Text = "00"
        txtStartmn.Text = "00"
        txtEndHour.Text = "23"
        txtEndmn.Text = "59"
    End Sub
    Private Sub FillComboBox(ByVal ComboBoxName As ComboBox, ByVal strValue As String)
        If ComboBoxName.FindString(strValue) < 0 Then
            ComboBoxName.Items.Add(strValue)
        End If
    End Sub
    Private Sub MossLogViewer_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        LogProgressBar.Visible = False
    End Sub
    Private Sub txtStartHour_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles txtStartHour.KeyPress
        If Not (Char.IsNumber(e.KeyChar) Or Char.IsControl(e.KeyChar)) Then
            e.Handled = True
        End If
    End Sub
    Private Sub txtStartmn_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles txtStartmn.KeyPress
        If Not (Char.IsNumber(e.KeyChar) Or Char.IsControl(e.KeyChar)) Then
            e.Handled = True
        End If
    End Sub
    Private Sub txtEndHour_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles txtEndHour.KeyPress
        If Not (Char.IsNumber(e.KeyChar) Or Char.IsControl(e.KeyChar)) Then
            e.Handled = True
        End If
    End Sub
    Private Sub txtEndmn_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles txtEndmn.KeyPress
        If Not (Char.IsNumber(e.KeyChar) Or Char.IsControl(e.KeyChar)) Then
            e.Handled = True
        End If
    End Sub
    ''' <summary>
    ''' Method to check if the file being open is a valid SharePoint log file.
    ''' </summary>
    ''' <param name="strFilename"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function IsSPLogFile(ByVal strFilename As String) As Boolean
        Dim LogReader As StreamReader = File.OpenText(strFilename)
        Dim strLine = LogReader.ReadLine()
        LogReader.Close()
        If strLine.IndexOf("Timestamp") = -1 Or strLine.IndexOf("Process") = -1 Or strLine.IndexOf("Area") = -1 Or strLine.IndexOf("Category") = -1 Then
            Return False
        Else
            Return True
        End If
    End Function
End Class
